package base.jdk8_demo.lambda.finaldemo;

import base.jdk8_demo.lambda.demo02.Emp;
import base.jdk8_demo.lambda.demo02.Status;
import base.jdk8_demo.lambda.demo07.TestStream;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zjw
 * @date 2022/4/3 9:35
 * @des 这是对jdk8中新特性的总结案例
 */
public class Demo01 {

    /**
     * Stream<Stream<T>>结构
     * 由.map()方法可知，匿名 Function.apply(string){}实现是通过TestStream::filterCharacter来实现的()
     */
    @Test
    public void case1(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        //list.stream().map()后结构{ {a,a,a}, {b,b,b},...}
        //list.stream().flatMap()后结构{a,a,a,b,b,b,.....}
        //由.map()方法可知，Function.apply(string){}实现是通过TestStream::filterCharacter来实现的()
        Stream<Stream<Character>> ssc = list.stream().map(str -> TestStream.filterCharacter(str));
//        ssc.forEach(stream -> System.out.println(stream));
        ssc.forEach(stream -> {
            stream.forEach(s-> System.out.print(s.toString().concat("-")));
            System.out.println();
        });
    }

    /**
     * 计算一批数的累加之和
     */
    @Test
    public void case2(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer sum = list.parallelStream()
                .reduce(0, (x, y) -> x + y);
        System.out.println("1-10之和: "+sum);

        Optional<Double> opt = emps1.stream()
                .map(Emp::getSalary)
                .reduce((x, y) -> x + y);
        System.out.println("所有员工总工资: "+opt.get());
    }

    /**
     * 集合->流的转换
     */
    @Test
    public void case3(){
        // TODO
    }

    /**
     * Stream.collect() 流->集合的转换 以及对流中元素的操作
     */
    @Test
    public void case4(){
        //转换为List
//        final List<Emp> emps = emps2.stream().collect(Collectors.toList());//Emp的List集合
        List<String> nameList = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.toList());
        System.out.println("提取name封装成List: "+nameList);

        //转换为Set
        Set<String> nameSet = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.toSet());
        System.out.println("提取name封装Set: "+nameSet);

        //转换为HashSet等自定义集合
        final HashSet<String> nameHashSet = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.toCollection(HashSet::new));
        System.out.println("提取name封装HashSet: "+nameHashSet);

        //对流中元素的操作
        String nameConcat = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.joining("%", "{", "}"));
        System.out.println("提取name并concat: "+nameConcat);
    }


    /**
     * 聚合统计
     */
    @Test
    public void case5(){
        //1、总数：总人数统计
        Long counts = emps2.stream()
                .collect(Collectors.counting());
//        counts = emps2.stream().count();
        System.out.println("总员工数: "+counts);

        //2、总和：工资总和
        Double sum = emps2.stream()
                .collect(Collectors.summingDouble(Emp::getSalary));
        System.out.println("工资总额: "+sum);

        //3、平均值：平均工资
        Double avg = emps2.stream()
                .collect(Collectors.averagingDouble(Emp::getSalary));
        System.out.println("平均工资: "+avg);

        //4、最大值：工资最高的员工
        Optional<Emp> max = emps2.stream()
                .collect(Collectors.maxBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary())));
//        max = emps2.stream().max((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()));
        System.out.println("工资最高的员工是: "+max.get());

        //5、最小值：员工最低工资
        Optional<Double> min = emps2.stream()
                .map(Emp::getSalary)
                .collect(Collectors.minBy(Double::compare));
        min = emps2.stream()
                .map(Emp::getSalary)
                .min(Double::compare);
        System.out.println("最低工资: "+ min.get());


        // 统计的另一种获取方式
        DoubleSummaryStatistics dss = emps2.stream()
                .collect(Collectors.summarizingDouble(Emp::getSalary));
        System.out.println("员工总数: "+ dss.getCount());
        System.out.println("工资总额: "+ dss.getSum());
        System.out.println("平均工资额: "+ dss.getAverage());
        System.out.println("最高工资: "+ dss.getMax());
        System.out.println("最低工资: "+ dss.getMin());
    }

    /**
     * 分组 和 多级分组
     */
    @Test
    public void case6(){
        /**
         {
             FREE=[
                Emp(id=null, name=ls, age=18, salary=3000.0, status=FREE),
                Emp(id=null, name=zl, age=19, salary=4000.0, status=FREE)
             ],
             BUSY=[
                Emp(id=null, name=dg, age=20, salary=5000.0, status=BUSY),
                Emp(id=null, name=tom, age=21, salary=6000.0, status=BUSY)
             ],
             VOCATON=[
                Emp(id=null, name=zs, age=16, salary=1000.0, status=VACATION),
                Emp(id=null, name=ww, age=17, salary=2000.0, status=VACATION)
             ]
         }
         */
        // 分组: 按照员工的 Status 分组
        Map<Status, List<Emp>> map = emps1.stream()
                .collect(Collectors.groupingBy(Emp::getStatus));
        System.out.println(map);


        /**
         {
             VOCATON={
                 青年=[
                    Emp(id=null, name=zs, age=16, salary=1000.0, status=VACATION),
                    Emp(id=null, name=ww, age=17, salary=2000.0, status=VACATION)
                 ]
             },
             FREE={
                 中年=[
                     Emp(id=null, name=ls, age=38, salary=3000.0, status=FREE),
                     Emp(id=null, name=zl, age=39, salary=4000.0, status=FREE)
                 ]
             },
             BUSY={
                 老年=[
                     Emp(id=null, name=dg, age=60, salary=5000.0, status=BUSY),
                     Emp(id=null, name=tom, age=61, salary=6000.0, status=BUSY)
                 ]
             }
         }
         */
        //多级分组: 先按Status，再按Age分组
        emps1 = Arrays.asList(
                new Emp("zs",16,1000.0, Status.VACATION),
                new Emp("ls", 38, 3000.0, Status.FREE),
                new Emp("ww", 17, 2000.0, Status.VACATION),
                new Emp("zl", 39, 4000.0, Status.FREE),
                new Emp("dg", 60, 5000.0, Status.BUSY),
                new Emp("tom", 61, 6000.0, Status.BUSY)
        );
        Map<Status, Map<String, List<Emp>>> mmap = emps1.stream()
                .collect(Collectors.groupingBy(Emp::getStatus, Collectors.groupingBy(emp->{
                    if (emp.getAge()<=35) {
                        return "青年";
                    }else if(emp.getAge()<=50){
                        return "中年";
                    }else{
                        return "老年";
                    }
                })));
        System.out.println(mmap);
    }

    /**
     * 分区: 满足条件的分到 true区, 不满足条件的分到 false 区
     * 按照工资分成 大于5000的和小于5000两个区
     */
    @Test
    public void case7(){
        /**
         {
             false=[
                 Emp(id=null, name=zs, age=16, salary=1000.0, status=VACATION),
                 Emp(id=null, name=ls, age=18, salary=3000.0, status=FREE),
                 Emp(id=null, name=ww, age=17, salary=2000.0, status=VACATION),
                 Emp(id=null, name=zl, age=19, salary=4000.0, status=FREE),
                 Emp(id=null, name=dg, age=20, salary=5000.0, status=BUSY)
             ],
             true=[
                Emp(id=null, name=tom, age=21, salary=6000.0, status=BUSY)
             ]
         }
         */
        Map<Boolean, List<Emp>> map = emps1.stream()
                .collect(Collectors.partitioningBy(emp -> emp.getSalary() > 5000));
        System.out.println(map);
    }


    private Map<String, Integer> map = new HashMap<>();
    {
        map.put("zs", 18);
        map.put("ls", 19);
        map.put("ww", 20);
    }
    List<Emp> list = Arrays.asList(
            new Emp(1,"zs", 18),
            new Emp(2,"ls", 19),
            new Emp(3,"ww", 20)
    );
    /**
     * Map 转 List
     * List 转 Map
     */
    @Test
    public void case8(){
        // Map 转 List
        List<Emp> emps = map.entrySet() //提取 Map.Entry 的 Set集合
                .stream()  //转换成流
                .map(e -> new Emp(e.getKey(), e.getValue())) // map 映射
                .collect(Collectors.toList());
        System.out.println(emps);

        // List 转 Map
        Map<String, Integer> map = list.stream()
                .collect(Collectors.toMap(e -> e.getName(), e -> e.getAge()));
        System.out.println(map);

        Map<Integer, Emp> empMap = list.stream()
                .collect(Collectors.toMap(e -> e.getId(), e -> e));
        System.out.println(empMap);

        // 故意给list中添加一个重复Emp.name
        list = Arrays.asList(
                new Emp(1,"zs", 18),
                new Emp(2,"ls", 19),
                new Emp(3,"ww", 20),
                new Emp(4,"ww", 21)
        );
        // key重复的情况，key有可能重复，会抛出异常：java.lang.illegalStateException:Duplicate key.
        // 这时候就要在toMap方法第三个入参中指定当前Key冲突时Value的选择，这里选择的是第二个Value覆盖第一个Value
        Map<String, Emp> eMap = list.stream()
                .collect(Collectors.toMap(e -> e.getName(), e -> e, (e1, e2) -> e2));
        // 下面演示一个重复Key后，新Value自定义组装逻辑如下
//                .collect(Collectors.toMap(
//                    Emp::getName,
//                    e -> e,
//                    (emp1, emp2) -> new Emp(emp1.getId(), emp1.getName()+emp2.getName(), emp2.getAge())
//                ));
        System.out.println(eMap);
    }

    List<Emp> emps1 = Arrays.asList(
            new Emp("zs",16,1000.0, Status.VACATION),
            new Emp("ls", 18, 3000.0, Status.FREE),
            new Emp("ww", 17, 2000.0, Status.VACATION),
            new Emp("zl", 19, 4000.0, Status.FREE),
            new Emp("dg", 20, 5000.0, Status.BUSY),
            new Emp("tom", 21, 6000.0, Status.BUSY)
    );
    List<Emp>  emps2 = Arrays.asList(
            new Emp("zs",16,1000.0, Status.VACATION),
            new Emp("ls", 18, 3000.0, Status.FREE),
            new Emp("ww", 17, 2000.0, Status.VACATION),
            new Emp("ww", 17, 2000.0, Status.BUSY)
    );


    /**
     * https://zhuanlan.zhihu.com/p/40966718
     *
     * jdk8 对 null的映射 - Optional,或者称之为 null的工具类
     */
    @Test
    public void test9(){
        // Optional总共提供三种静态方法来构造一个Optional
        //方式一： Optional.of(T value)， value绝对不能为null，否则抛异常
        Optional<String> opt1 = Optional.of("");
        // 方式二：Optional.ofNullable(T value), value可为null
        Optional<Object> opt2 = Optional.ofNullable(null);
        System.out.println(opt2);
        // 方式三：Optional.empty() 创建一个空的 Optional
        Optional<Object> opt3 = Optional.empty();


    }

}
